package com.br.generateswingview.core;

import java.awt.Component;
import java.awt.Container;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.reflect.Field;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.text.DefaultFormatterFactory;
import javax.swing.text.MaskFormatter;
import javax.swing.text.NumberFormatter;

import com.br.dnaklass.core.FamilyTree;
import com.br.dnaklass.core.ReflectTools;
import com.br.generateswingview.annotations.Alias;
import com.br.generateswingview.annotations.Ignore;
import com.br.generateswingview.annotations.MaskedField;

@SuppressWarnings( "rawtypes" )
public abstract class GViewForm<T> extends JDialog {
	private static final long serialVersionUID = 1L;
	private T object;
	private Container panel;
	private final Class klass;

	public GViewForm( Class klass ) {
		this.klass = klass;
		mountView();
	}

	public T getObject() {
		return object;
	}

	private void mountView() {
		int length = 0;
		setTitle( klass.getSimpleName() );
		setModal( true );

		this.panel = new JPanel( new GridLayout( 0, 2 ) );

		List<Class> parents = FamilyTree.getParents( klass );
		for ( Class c : parents )
			for ( Field field : c.getDeclaredFields() )
				if ( field.getAnnotation( Ignore.class ) == null ) {
					length++;
					panel.add( createLabel( field ) );
					panel.add( createField( field ) );
				}

		initFrame( length );
		JButton btnSave = new JButton( "Salvar" );
		JButton btnClear = new JButton( "Limpar" );
		setActionSave( btnSave );
		setActionClear( btnClear );
		panel.add( btnSave );
		panel.add( btnClear );
		getContentPane().add( panel );
	}

	private void setActionClear( JButton btnClear ) {
		btnClear.addActionListener( new ActionListener() {
			@Override
			public void actionPerformed( ActionEvent e ) {
				clearFields();
			}

		} );
	}

	public void clearFields() {
		for ( Component component : panel.getComponents() )
			if ( component instanceof JFormattedTextField )
				( (JFormattedTextField) component ).setValue( null );
			else if ( component instanceof JTextField )
				( (JTextField) component ).setText( "" );
			else if ( component instanceof JComboBox ) {
				JComboBox combo = (JComboBox) component;
				if ( combo.getItemCount() > 0 )
					combo.setSelectedIndex( 0 );
			}
	}

	private void setActionSave( JButton btnSave ) {
		btnSave.addActionListener( new ActionListener() {

			@Override
			public void actionPerformed( ActionEvent arg0 ) {
				List<Class> parents = FamilyTree.getParents( klass );
				object = ReflectTools.instance( klass );
				for ( Class c : parents )
					for ( Field field : c.getDeclaredFields() )
						if ( field.getAnnotation( Ignore.class ) == null ) {
							field.setAccessible( true );
							String name = field.getName();
							Component component = GVUtils.getElementByName( GViewForm.this, name );
							Class type = c = field.getType();
							try {
								if ( component instanceof JComboBox )
									field.set( klass.cast( object ), type.cast( ( (JComboBox) component ).getSelectedItem() ) );
								else if ( component instanceof JTextField ) {
									JTextField textField = (JTextField) component;
									if ( type.equals( String.class ) )
										field.set( klass.cast( object ), textField.getText() );
									else if ( type.equals( int.class ) )
										field.set( klass.cast( object ), Integer.parseInt( textField.getText() ) );
									else if ( type.equals( float.class ) ) {
										String text = textField.getText();
										text = text.replaceAll( ",", "." );
										field.set( klass.cast( object ), Float.parseFloat( text ) );
									} else if ( type.equals( double.class ) ) {
										String text = textField.getText();
										text = text.replaceAll( ",", "." );
										field.set( klass.cast( object ), Double.parseDouble( text ) );
									} else if ( type.equals( char.class ) )
										field.set( klass.cast( object ), textField.getText().charAt( 0 ) );
									else if (type.equals( Date.class )){
										String mask = "dd/MM/yyyy";
										if(field.getAnnotation( MaskedField.class ) != null)
											mask = field.getAnnotation( MaskedField.class ).mask();
										SimpleDateFormat sf = new SimpleDateFormat(mask);
										try {
											field.set(klass.cast( object ), sf.parseObject(textField.getText()));
										} catch (ParseException e) {
											e.printStackTrace();
										}
									}
								}
							} catch ( IllegalArgumentException e ) {
							} catch ( IllegalAccessException e ) {
							}
						}
				try {
					endAction();
				} catch ( Throwable e ) {
					JOptionPane.showMessageDialog( GViewForm.this, e.getMessage(), "Erro", JOptionPane.ERROR_MESSAGE, null );
				}
			}

		} );
	}

	public abstract void endAction();

	private void initFrame( int length ) {
		setResizable( false );
		this.setSize( 500, 30 * ( length + 1 ) );
		setLocationRelativeTo( null );
		setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE );
	}

	private Component createField( Field field ) {
		Component textField;
		if ( field.getAnnotation( MaskedField.class ) != null ) {
			MaskFormatter mask = new MaskFormatter();
			try {
				mask.setMask( field.getAnnotation( MaskedField.class ).mask() );
			} catch ( ParseException e ) {
				e.printStackTrace();
			}
			textField = new JFormattedTextField();
			mask.setAllowsInvalid( false );
			( (JFormattedTextField) textField ).setFormatterFactory( new DefaultFormatterFactory( mask ) );
		} else if ( field.getType() == String.class ){
			textField = new JTextField();
		}else if ( field.getType().equals( int.class ) ) {
			DecimalFormat dFormat = new DecimalFormat( "#######" );
			NumberFormatter Formatter = new NumberFormatter( dFormat );
			Formatter.setFormat( dFormat );
			Formatter.setAllowsInvalid( false );
			textField = new JFormattedTextField();
			( (JFormattedTextField) textField ).setFormatterFactory( new DefaultFormatterFactory( Formatter ) );
		} else if ( field.getType().equals( float.class ) || field.getType().equals( double.class ) ) {
			DecimalFormat dFormat = new DecimalFormat( "########.00" );
			NumberFormatter Formatter = new NumberFormatter( dFormat );
			Formatter.setFormat( dFormat );
			Formatter.setAllowsInvalid( true );
			textField = new JFormattedTextField();
			( (JFormattedTextField) textField ).setFormatterFactory( new DefaultFormatterFactory( Formatter ) );
		} else if( field.getType().equals(Date.class)){
			String maskValue = "##/##/####";
			if(field.getAnnotation(MaskedField.class) != null){
				 maskValue = field.getAnnotation( MaskedField.class ).mask();
			}
			MaskFormatter mask = new MaskFormatter();
			try {
				mask.setMask( maskValue );
			} catch (ParseException e) {
				e.printStackTrace();
			}
			textField = new JFormattedTextField();
			mask.setAllowsInvalid( false );
			( (JFormattedTextField) textField ).setFormatterFactory( new DefaultFormatterFactory( mask ) );	
		} else if ( !field.getType().isPrimitive() ){
			textField = new JComboBox();
		}else{
			textField = new JTextField();
		}
		textField.setName( field.getName() );

		return textField;
	}

	private static JLabel createLabel( Field field ) {
		JLabel label = new JLabel();
		label.setName( "L".concat( field.getName() ) );
		if ( field.getAnnotation( Alias.class ) != null ) {
			Alias alias = field.getAnnotation( Alias.class );
			label.setText( alias.value() );
		} else {
			String name = field.getName();
			String alias = name.substring( 0, 1 ).toUpperCase();
			alias += field.getName().substring( 1, name.length() );
			label.setText( alias );
		}
		return label;
	}
}
